using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using System.Globalization;
using System.Management;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.BizTalk.ExplorerOM;

namespace BizTalk.BuildGenerator.Tasks
{
    /// <summary>
    /// The service status codes
    /// </summary>
    internal class ServiceStatus
    {
        internal const string ReadyToRun = "1";
        internal const string Active = "2";
        internal const string SuspendedResumable = "4";
        internal const string Dehydrated = "8";
        internal const string CompletedWithDiscardedMessages = "16";
        internal const string SuspendedNotResumable = "32";
        internal const string InBreakpoint = "64";
    }
    /// <summary>
    /// Cleans all messages and service instances from a host queue
    /// </summary>
    public class CleanHostQueue : Task
    {
        private string _HostName;
        /// <summary>
        /// The name of the host to clean
        /// </summary>
        public string HostName
        {
            get { return _HostName; }
            set { _HostName = value; }
        }
        /// <summary>
        /// Executes the build task
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            Logger.LogMessage(this, "Executing task: " + this.GetType().FullName);
            Logger.LogMessage(this, "Host: " + _HostName);
            Clean(_HostName);
            Logger.LogMessage(this, "Finished Executing task: " + this.GetType().FullName);
            return true;
        }
        /// <summary>
        /// Overrides the execute
        /// </summary>
        /// <returns></returns>
        public void Clean(string hostName)
        {
            int noMessageInstances = CountMessageInstances(hostName);
            if (noMessageInstances > 0)
            {
                SuspendMessageInstances(hostName);
                TerminateMessageInstances(hostName);
                noMessageInstances = CountMessageInstances(hostName);
                if (noMessageInstances > 0)
                    Trace.WriteLine("There are still message instances on the queue");
            }

            int noServiceInstances = CountServiceInstances(hostName);
            if (noServiceInstances > 0)
            {
                SuspendServiceInstances(hostName);
                TerminateServiceInstances(hostName);
                noServiceInstances = CountServiceInstances(hostName);
                if (noServiceInstances > 0)
                    Trace.WriteLine("There are still service instances on the queue");
            }
        }
        /// <summary>
        /// Determines if the instance can be terminated
        /// </summary>
        /// <param name="status"></param>
        /// <returns></returns>
        private bool CanTerminate(string status)
        {
            if (status == ServiceStatus.CompletedWithDiscardedMessages
                || status == ServiceStatus.SuspendedNotResumable
                || status == ServiceStatus.SuspendedResumable)
                return true;
            else
                return false;
        }
        /// <summary>
        /// Determines if the instance can be terminated
        /// </summary>
        /// <param name="status"></param>
        /// <returns></returns>
        private bool CanSuspend(string status)
        {
            if (status != ServiceStatus.CompletedWithDiscardedMessages
                && status != ServiceStatus.SuspendedNotResumable
                && status != ServiceStatus.SuspendedResumable)
                return true;
            else
                return false;
        }
        /// <summary>
        /// Records to the trace the action
        /// </summary>
        /// <param name="instanceId"></param>
        /// <param name="typeId"></param>
        /// <param name="classId"></param>
        /// <param name="action"></param>
        private void TraceAction(string instanceId, string typeId, string classId, string action)
        {
            List<string> args = new List<string>();
            args.Add(instanceId);
            args.Add(typeId);
            args.Add(classId);
            args.Add(action);
            string message = string.Format("Instance ID: {0}, Action: {3}, Type: {1}, Class: {2}", args.ToArray());
            Logger.LogMessage(this, message);
        }
        /// <summary>
        /// Displays the message instances currently on this queue
        /// </summary>
        /// <param name="hostName"></param>
        private int CountMessageInstances(string hostName)
        {
            int count = 0;

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, "Display Message Instances");
            Logger.LogMessage(this, string.Empty);

            string query = string.Format(CultureInfo.CurrentCulture, "SELECT * FROM MSBTS_MessageInstance");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(new ManagementScope(@"root\MicrosoftBizTalkServer"), new WqlObjectQuery(query), null);

            foreach (ManagementObject messageInstanceManager in searcher.Get())
            {
                string host = messageInstanceManager["HostName"].ToString();
                string status = messageInstanceManager["ServiceInstanceStatus"].ToString();

                if (host == hostName)
                {
                    string serviceInstanceId = messageInstanceManager["ServiceInstanceID"].ToString();
                    Logger.LogMessage(this, "Service Instance: " + serviceInstanceId + " is " + status);
                    count++;

                }
            }

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, count.ToString() + " Message Instances in queue");
            Logger.LogMessage(this, string.Empty);
            return count;
        }
        /// <summary>
        /// This will terminate all message instances belonging to a host
        /// </summary>
        /// <param name="hostName"></param>
        private void TerminateMessageInstances(string hostName)
        {
            int count = 0;

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, "Terminate Message Instances");
            Logger.LogMessage(this, string.Empty);

            string query = string.Format(CultureInfo.CurrentCulture, "SELECT * FROM MSBTS_MessageInstance");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(new ManagementScope(@"root\MicrosoftBizTalkServer"), new WqlObjectQuery(query), null);

            foreach (ManagementObject messageInstanceManager in searcher.Get())
            {
                string host = messageInstanceManager["HostName"].ToString();
                string status = messageInstanceManager["ServiceInstanceStatus"].ToString();

                if (host == hostName)
                {
                    if (CanTerminate(status))
                    {
                        string serviceInstanceId = messageInstanceManager["ServiceInstanceID"].ToString();
                        string serviceTypeId = messageInstanceManager["ServiceTypeId"].ToString();
                        string serviceClassId = messageInstanceManager["ServiceTypeId"].ToString();  //For some reason the service class id always seems to return nullref exception and  and the service type seems to work if used instead, dont know why?

                        TerminateServiceInstancesByID(hostName, serviceInstanceId, serviceClassId, serviceTypeId);
                        TraceAction(serviceInstanceId, serviceTypeId, serviceClassId, "Terminate Message Instance");
                        count++;
                    }
                }
            }

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, count.ToString() + " Message Instances have been terminated");
            Logger.LogMessage(this, string.Empty);
        }
        /// <summary>
        /// This will suspend all message instances belonging to a host
        /// </summary>
        /// <param name="hostName"></param>
        private void SuspendMessageInstances(string hostName)
        {
            int count = 0;

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, "Suspend Message Instances");
            Logger.LogMessage(this, string.Empty);

            string query = string.Format(CultureInfo.CurrentCulture, "SELECT * FROM MSBTS_MessageInstance");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(new ManagementScope(@"root\MicrosoftBizTalkServer"), new WqlObjectQuery(query), null);

            foreach (ManagementObject messageInstanceManager in searcher.Get())
            {
                string host = messageInstanceManager["HostName"].ToString();
                string status = messageInstanceManager["ServiceInstanceStatus"].ToString();

                if (host == hostName)
                {
                    if (CanSuspend(status))
                    {
                        string serviceInstanceId = messageInstanceManager["ServiceInstanceID"].ToString();
                        string serviceTypeId = messageInstanceManager["ServiceTypeId"].ToString();
                        string serviceClassId = messageInstanceManager["ServiceTypeId"].ToString();        //For some reason the service class id always seems to return nullref exception and      and the service type seems to work if used instead, dont know why?             

                        SuspendServiceInstancesByID(hostName, serviceInstanceId, serviceClassId, serviceTypeId);
                        TraceAction(serviceInstanceId, serviceTypeId, serviceClassId, "Suspend Message Instance");
                        count++;
                    }
                }
            }

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, count.ToString() + " Message Instances have been suspended");
            Logger.LogMessage(this, string.Empty);
        }
        /// <summary>
        /// Suspends all service instances for a host
        /// </summary>
        /// <param name="hostName"></param>
        private int CountServiceInstances(string hostName)
        {
            int count = 0;

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, "Display Service Instances");
            Logger.LogMessage(this, string.Empty);

            string query = string.Format(CultureInfo.CurrentCulture, "SELECT * FROM MSBTS_ServiceInstance");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(new ManagementScope(@"root\MicrosoftBizTalkServer"), new WqlObjectQuery(query), null);

            foreach (ManagementObject serviceInstanceManager in searcher.Get())
            {
                string host = serviceInstanceManager["HostName"].ToString();
                string status = serviceInstanceManager["ServiceStatus"].ToString();

                if (host == hostName)
                {

                    string serviceInstanceId = serviceInstanceManager["InstanceID"].ToString();
                    Logger.LogMessage(this, "Service Instance: " + serviceInstanceId + " is " + status);
                    count++;
                }
            }

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, count.ToString() + " Service Instances currently in queue");
            Logger.LogMessage(this, string.Empty);
            return count;
        }
        /// <summary>
        /// Suspends all service instances for a host
        /// </summary>
        /// <param name="hostName"></param>
        private void SuspendServiceInstances(string hostName)
        {
            int count = 0;

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, "Suspend Service Instances");
            Logger.LogMessage(this, string.Empty);

            string query = string.Format(CultureInfo.CurrentCulture, "SELECT * FROM MSBTS_ServiceInstance");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(new ManagementScope(@"root\MicrosoftBizTalkServer"), new WqlObjectQuery(query), null);

            foreach (ManagementObject serviceInstanceManager in searcher.Get())
            {
                string host = serviceInstanceManager["HostName"].ToString();
                string status = serviceInstanceManager["ServiceStatus"].ToString();

                if (host == hostName)
                {
                    if (CanSuspend(status))
                    {
                        string serviceInstanceId = serviceInstanceManager["InstanceID"].ToString();
                        string serviceTypeId = serviceInstanceManager["ServiceTypeId"].ToString();
                        string serviceClassId = serviceInstanceManager["ServiceTypeId"].ToString();  //For some reason the service class id always seems to return nullref exception and  and the service type seems to work if used instead, dont know why?

                        SuspendServiceInstancesByID(hostName, serviceInstanceId, serviceClassId, serviceTypeId);
                        TraceAction(serviceInstanceId, serviceTypeId, serviceClassId, "Terminate Message Instance");
                        count++;
                    }
                }
            }

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, count.ToString() + " Service Instances have been suspended");
            Logger.LogMessage(this, string.Empty);
        }
        /// <summary>
        /// Terminates all service instances for a host
        /// </summary>
        /// <param name="hostName"></param>
        private void TerminateServiceInstances(string hostName)
        {
            int count = 0;

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, "Terminate Service Instances");
            Logger.LogMessage(this, string.Empty);

            string query = string.Format(CultureInfo.CurrentCulture, "SELECT * FROM MSBTS_ServiceInstance");
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(new ManagementScope(@"root\MicrosoftBizTalkServer"), new WqlObjectQuery(query), null);

            foreach (ManagementObject serviceInstanceManager in searcher.Get())
            {
                string host = serviceInstanceManager["HostName"].ToString();
                string status = serviceInstanceManager["ServiceStatus"].ToString();

                if (host == hostName)
                {
                    if (CanTerminate(status))
                    {
                        string serviceInstanceId = serviceInstanceManager["InstanceID"].ToString();
                        string serviceTypeId = serviceInstanceManager["ServiceTypeId"].ToString();
                        string serviceClassId = serviceInstanceManager["ServiceTypeId"].ToString();  //For some reason the service class id always seems to return nullref exception and the service type seems to work if used instead, dont know why?

                        TerminateServiceInstancesByID(hostName, serviceInstanceId, serviceClassId, serviceTypeId);
                        TraceAction(serviceInstanceId, serviceTypeId, serviceClassId, "Terminate Service Instance");
                        count++;
                    }
                }
            }

            Logger.LogMessage(this, string.Empty);
            Logger.LogMessage(this, count.ToString() + " Service Instances have been terminated");
            Logger.LogMessage(this, string.Empty);
        }
        /// <summary>
        /// Suspends one service instance
        /// </summary>
        /// <param name="hostName"></param>
        /// <param name="serviceInstanceId"></param>
        /// <param name="serviceClassId"></param>
        /// <param name="serviceTypeId"></param>
        private void SuspendServiceInstancesByID(string hostName, string serviceInstanceId, string serviceClassId, string serviceTypeId)
        {
            List<string> serviceInstanceIdList = new List<string>();
            List<string> serviceClassIdList = new List<string>();
            List<string> serviceTypeIdList = new List<string>();

            serviceInstanceIdList.Add(serviceInstanceId);
            serviceClassIdList.Add(serviceClassId);
            serviceTypeIdList.Add(serviceTypeId);


            string managementObjectPath = string.Format(CultureInfo.CurrentCulture, "root\\MicrosoftBizTalkServer:MSBTS_HostQueue.HostName=\"{0}\"", hostName);
            ManagementObject managementObject = new ManagementObject(managementObjectPath);

            try
            {
                managementObject.InvokeMethod("SuspendServiceInstancesByID", new object[] { serviceClassIdList.ToArray(), serviceTypeIdList.ToArray(), serviceInstanceIdList.ToArray() });
            }
            catch (COMException comEx)
            {
                List<string> args = new List<string>();
                args.Add(hostName);
                args.Add(serviceInstanceId);
                args.Add(serviceClassId);
                args.Add(serviceTypeId);
                args.Add(comEx.ToString());
                string message = string.Format("Com Error -- Host: {0}, Instance: {1}, Class: {2}, Type: {3}, Error: {4}", args.ToArray());
                Logger.LogMessage(this, message);
                if (comEx.Message != "Suspend request could not be sent for the specified instance.  The instance might not exist in any of the MessageBoxes, or database failure might have been encountered.")
                    throw;
            }
        }
        /// <summary>
        /// Terminates one service instance
        /// </summary>
        /// <param name="hostName"></param>
        /// <param name="serviceInstanceId"></param>
        /// <param name="serviceClassId"></param>
        /// <param name="serviceTypeId"></param>
        private void TerminateServiceInstancesByID(string hostName, string serviceInstanceId, string serviceClassId, string serviceTypeId)
        {
            List<string> serviceInstanceIdList = new List<string>();
            List<string> serviceClassIdList = new List<string>();
            List<string> serviceTypeIdList = new List<string>();

            serviceInstanceIdList.Add(serviceInstanceId);
            serviceClassIdList.Add(serviceClassId);
            serviceTypeIdList.Add(serviceTypeId);


            string managementObjectPath = string.Format(CultureInfo.CurrentCulture, "root\\MicrosoftBizTalkServer:MSBTS_HostQueue.HostName=\"{0}\"", hostName);
            ManagementObject managementObject = new ManagementObject(managementObjectPath);

            try
            {
                managementObject.InvokeMethod("TerminateServiceInstancesByID", new object[] { serviceClassIdList.ToArray(), serviceTypeIdList.ToArray(), serviceInstanceIdList.ToArray() });
            }
            catch (COMException comEx)
            {
                List<string> args = new List<string>();
                args.Add(hostName);
                args.Add(serviceInstanceId);
                args.Add(serviceClassId);
                args.Add(serviceTypeId);
                args.Add(comEx.ToString());
                string message = string.Format("Com Error -- Host: {0}, Instance: {1}, Class: {2}, Type: {3}, Error: {4}", args.ToArray());
                Logger.LogMessage(this, message);
                if (comEx.Message != "Terminate request could not be sent for the specified instance.  The instance might not exist in any of the MessageBoxes, or database failure might have been encountered.")
                    throw;
            }
        }
    }
}



